package com.tal.algorithms.tree.binarysearchtree;

import java.io.Serializable;
import java.util.List;

public class BinarySearchTree<Key extends Comparable & Serializable,Value> implements Tree<Key>{
    private Node<Key, Value> node;
    private BinarySearchTree<Key, Value> left;
    private BinarySearchTree<Key, Value> right;

    class Node<T extends Key, E extends Value> {
        private T key;
        private E value;

        public Node(T key, E value) {
            this.key = key;
            this.value = value;
        }

        public T getKey() {
            return key;
        }

        public void setKey(T key) {
            this.key = key;
        }

        public E getValue() {
            return value;
        }

        public void setValue(E value) {
            this.value = value;
        }
    }

    public void remove(Node<Key, Value> node) {
        remove(this, node);
    }

    @SuppressWarnings("unchecked")
    public BinarySearchTree<Key, Value> remove(BinarySearchTree<Key, Value> binarySearchTree, Node<Key, Value> node) {
        if (binarySearchTree == null || node == null) {
            return null;
        }
        BinarySearchTree current = binarySearchTree;
        BinarySearchTree parent = null;
        while (current != null) {
            if (node.getKey().compareTo(current.getKey()) < 0) {
                parent = current;
                current = binarySearchTree.getLeft();
            } else if (node.getKey().compareTo(current.getKey()) > 0){
                parent = current;
                current = binarySearchTree.getRight();
            } else if (current.getLeft() != null && current.getRight() != null){
                //当结点的左右子树都不空的时候，一般的删除策略是用其右子树的最小数据
                //容易找到）代替要删除的结点数据并递归删除该结点（此时为null），因为
                //右子树的最小结点不可能有左孩子，所以第二次删除较为容易。
                current.setNode(current.getRight().getMin());
                current.setRight(remove(current.getRight(),  current.getNode()));
            } else {
                current = current.getLeft() != null ? current.getLeft() : current.getRight();
            }
        }

        return binarySearchTree;
    }

    /**
     * 插入
     */
    public void insert(Node<Key, Value> node) throws IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException("Cannot insert null entity.");
        }
        insert(this, node);
    }

    public void insert(Key key, Value value) {
        insert(this, new Node<>(key, value));
    }

    public void insert(BinarySearchTree<Key, Value> binarySearchTree, Node<Key, Value> node) {
        if (binarySearchTree == null) {
            throw new IllegalArgumentException("BinarySearchTree must not be null.");
        }

        BinarySearchTree<Key,Value> current = binarySearchTree;
        BinarySearchTree<Key, Value> parent = binarySearchTree;
        boolean isLeftChild = false;
        while (current != null) {
            parent = current;
            if (node.getKey().compareTo(current.getKey()) < 0) {
                current = current.getLeft();
                isLeftChild = true;
            } else {
                current = current.getRight();
            }
        }
        BinarySearchTree<Key, Value> insertTree = new BinarySearchTree<>(node);
        if (isLeftChild) {
            parent.setLeft(insertTree);
        } else {
            parent.setRight(insertTree);
        }
    }

    /**
     * 递归实现，资源消耗往往比较高
     */
    public BinarySearchTree<Key, Value> search(BinarySearchTree<Key, Value> binarySearchTree, Key key) {
        if (binarySearchTree == null || key == binarySearchTree.getKey()) {
            return binarySearchTree;
        }
        if (key.compareTo(binarySearchTree.getKey()) < 0) {
            return search(binarySearchTree.getLeft(), key);
        } else {
            return search(binarySearchTree.getRight(), key);
        }
    }

    public BinarySearchTree<Key, Value> search(Key key) {
        return search(this, key);
    }

    /**
     * 迭代的方式往往更加省力
     */
    public BinarySearchTree<Key, Value> iterativeTreeSearch(BinarySearchTree<Key, Value> binarySearchTree, Key key) {
        while (binarySearchTree != null && key.compareTo(binarySearchTree.getKey()) != 0) {
            if (key.compareTo(binarySearchTree.getKey())  < 0) {
                binarySearchTree = binarySearchTree.getLeft();
            } else {
                binarySearchTree = binarySearchTree.getRight();
            }
        }
        return binarySearchTree;
    }

    public BinarySearchTree<Key, Value> iterativeTreeSearch(Key key) {
        return iterativeTreeSearch(this, key);
    }

    public Node<Key, Value> getMin() {
        BinarySearchTree<Key, Value> binarySearchTree = this;
        while (binarySearchTree.getLeft() != null) {
            binarySearchTree = binarySearchTree.getLeft();
        }
        return binarySearchTree.getNode();
    }

    public Node<Key, Value> getMax() {
        BinarySearchTree<Key, Value> binarySearchTree = this;
        while (binarySearchTree.getRight() != null) {
            binarySearchTree = binarySearchTree.getRight();
        }
        return binarySearchTree.getNode();
    }

    /**
     * 后继：如果所有的关键字互不相同，则一个节点x的后继是大于x.key的最小关键字的节点
     */
    public Node<Key, Value> successor(Key key) {
        // 在二叉搜索树中查找 key 对应的节点
        BinarySearchTree<Key, Value> binarySearchTree = search(key);

        // 如果 key 对应的节点不存在，则 key 没有后继，返回 null
        if (binarySearchTree == null)
            return null;

        // 如果key所在的节点右子树不为空,则其右子树的最小值为key的后继
        if (binarySearchTree.getRight() != null) {
            return binarySearchTree.getRight().getMin();
        }

        // 如果右边的树不存在，那么就找root节点到自己这个节点的路径上有没有后继
        return successorFromAncestor(this, key);
    }

    private Node<Key, Value> successorFromAncestor(BinarySearchTree<Key, Value> binarySearchTree, Key key){
        //该key就是根节点。
        if (key.compareTo(binarySearchTree.getKey()) == 0){
            return null;
        }
        if (key.compareTo(binarySearchTree.getKey()) > 0 ){
            //key 在右边
            return successorFromAncestor(binarySearchTree.getRight(), key);
        }else {
            // key 在左边
            Node minNode = successorFromAncestor(binarySearchTree.getLeft(), key);
            if (minNode != null)
                // minNode和当前节点node取最小值返回
                return minNode.getKey().compareTo(binarySearchTree.getKey()) > 0 ? binarySearchTree.getNode() : minNode;
            else
                // 如果minNode为空, 则当前节点即为结果
                return binarySearchTree.getNode();
        }
    }

    /**
     * 随机构建二叉树
     */
    public BinarySearchTree(List<Node<Key, Value>> nodeList) {
        if (nodeList == null || nodeList.isEmpty()) {
            throw new IllegalArgumentException("Illegal node list.");
        }

        BinarySearchTree<Key,Value> root = new BinarySearchTree<Key, Value>(nodeList.get(0));

        for (int i = 1; i < nodeList.size(); i++) {
            root.insert(nodeList.get(i));
        }
    }

    public BinarySearchTree(Node<Key, Value> node) {
        this.node = node;
    }

    public BinarySearchTree(Node<Key, Value> node, BinarySearchTree<Key, Value> left, BinarySearchTree<Key, Value> right) {
        this.node = node;
        this.left = left;
        this.right = right;
    }

    public Node<Key, Value> getNode() {
        return node;
    }

    public void setNode(Node<Key, Value> node) {
        this.node = node;
    }

    @Override
    public Key getKey() {
        return getNode() != null ? getNode().getKey() : null;
    }

    @Override
    public void setKey(Key key) {
        if (getNode() != null) {
            this.getNode().setKey(key);
        }
    }

    public <T extends BinarySearchTree<Key, Value>> void setRight(T right) {
        this.right = right;
    }

    public <T extends BinarySearchTree<Key, Value>> void setLeft(T left) {
        this.left = left;
    }

    @SuppressWarnings("unchecked")
    @Override
    public BinarySearchTree<Key, Value> getLeft() {
        return left;
    }

    @SuppressWarnings("unchecked")
    @Override
    public BinarySearchTree<Key, Value> getRight() {
        return right;
    }

}
